import os
import stat
import P4
import sys
import logging

class s3dP4:
   def __init__ (self, port = "", user = "", workspace = "", isOff = False, useLogs = False):
      self.useLogs = useLogs
      self.lasterror = ""
      self.useLogging = False
      
      if isOff:           
         self.p4 = None
         self.LogLine("warning perforce is off")
         return
         
      p4 = P4.P4()
      
      p4.port    = str(port)
      p4.user    = str(user)
      p4.client  = str(workspace)
      p4.charset = "utf8"
      p4.exception_level = 0 #no exceptions
      
      p4.connect()
      
      self.p4 = p4
      self.LogResult()

   def Term(self):
      if self.p4: self.p4.disconnect()  
      p4 = None

   def HasErrors (self):
      if not self.p4: return False
      return len(self.p4.errors) > 0

   def GetErrors (self):
      if not self.p4: return []
      return self.p4.errors

   def GetWarnings (self):
      if not self.p4: return []
      return self.p4.warnings

   def LogResult(self):
      if not self.useLogs: return
      if not self.p4:      return
      
      if (len(self.p4.errors) > 0):
         if self.useLogging:
            logging.error(str(self.p4.errors))
         else:
            print "errors:   ", self.p4.errors
      
      if (len(self.p4.warnings) > 0):
         if self.useLogging:
            logging.warning(str(self.p4.warnings))
         else:
            print "warnings: ", self.p4.warnings

   def SetUseLogging(self, flag):
      self.useLogging = flag

   def LogLine(self, line):
      if self.useLogs:
         if self.useLogging:
            logging.info(line)
         else:
            print line

   #Checkout tests
   #Test if file checkouted by local user
   #True - no checkout
   #False - checkouted
   def IsCheckoutedByUser(self, filename):
      if self.p4:
         res = self.p4.run("fstat", filename)
         self.LogResult()
         if len(res)>0:
            for res_dic in res:
               if res_dic.has_key('actionOwner'):
                  opened_by = res_dic['actionOwner']
                  #if (opened_by == self.p4.user):
                  self.LogLine("File '" + filename + "' checkouted by " + str(opened_by))
                  return False
      return True

   #Test if file checkouted by someone else
   #True - no checkout
   #False - checkouted
   def IsCheckouted(self, filename):
      if self.p4:
         res = self.p4.run("fstat", filename)
         self.LogResult()
         if len(res)>0:
            res_dic = res[0]
            if res_dic.has_key('otherOpens') and res_dic.has_key('otherOpen'):
               if self.useLogs:
                  opened_by = res_dic['otherOpens']
                  self.LogLine("File '" + filename + "' checkouted by " + str(opened_by) + " user(s)")
                  self.LogLine(str(res_dic['otherOpen']))
               return False
      return True

   #Base sync (supports file and directories)
   #True  - sync ok
   #False - sync failed
   def Sync(self, file, isForce = True, changeList = "", bypassChange = False):
      if not self.p4: 
         return True
      
      file = file.replace('\\', '/')
      
      if changeList == "":
         fileParam = file + "#head"
      else:
         fileParam = file + changeList
         
      self.LogLine("Sync " + str(fileParam))

      if isForce:
         if bypassChange:
            self.p4.run("sync", "-f", "-k", fileParam)
         else:
            self.p4.run("sync", "-f", fileParam)
      else:
         if bypassChange:
            self.p4.run("sync", "-k", fileParam)
         else:
            self.p4.run("sync", fileParam)

      self.LogResult()

      return (not self.HasErrors())

   def Add(self, file, comment = "added new file"):
      if not self.p4: 
         return True
      
      spec = self.p4.fetch_change()
      spec[ "Description" ] = comment
      spec._files = []
      self.p4.input = spec
      res_str = self.p4.run_change("-i")
      
      if (len(res_str) > 0):
         res_str = res_str[0]
         #extract change list num from string 'Changelist 3333 created.'
         clistNum = res_str[res_str.find(' ') + 1:res_str.find(' created')]
         self.p4.run("add", "-c" + clistNum, "-f", file)
         self.LogResult()
         self.p4.run("submit", "-c" + clistNum)
         self.LogResult()
         return True
      else:
         self.LogLine("failed to create changelist (add file)")
         return False

   # not working with dirs, supports filelists, separated by space ...
   def CheckOut(self, files, isForce = True, changelist = -1):
      files = files.replace('\\', '/')
      fileList = files.split(" ")
      for file in fileList:
         if not self.p4:
            #make writeable
            if not os.access(file, os.F_OK):
               self.LogLine("Checkout file " + str(file) + " doesn't exsist")
            else:
               stats = os.stat(file)
               if len(stats) > 0:
                  self.LogLine("Checkout file " + str(file) + " (R\O removed)")
                  os.chmod(file, stat.S_IWRITE)
         else:
            self.LogLine("Checkout " + str(file))
            
            #first get latest with force
            if isForce:
               self.p4.run("sync", "-f", file + "#head")
               self.LogResult()
            
            if changelist == -1:   
               self.p4.run("edit", file)
            else:
               self.p4.run("edit", "-c" + changelist, file)
            self.LogResult()
      
      return self.HasErrors()

   def CheckIn(self, files, comment = "default"):
      files = files.replace('\\', '/')
      fileList = files.split(" ")
      depotFiles = []
      for file in fileList:
         if not self.p4:
            #make readonly
            if not os.access(file, os.F_OK):
               self.LogLine("Checkin failed! File "+str(file)+" doesn't exsist")
            else:
               stats = os.stat(file)
               if len(stats) > 0:
                  self.LogLine("Checkin file "+str(file)+" (R\O set)")
                  os.chmod(file, stat.S_IREAD)
         else:
            file_stat = self.p4.run("fstat", file)
            if (len(file_stat) <= 0):
               self.LogLine("no such file in depot "+str(file))
               if os.access(file, os.F_OK):
                  self.LogLine("adding file to perforce "+str(file))
                  self.p4.run("add", "-f", file)
                  depotFiles.append(self.p4.run("fstat", file)[0]['depotFile'])
               else:
                  self.LogLine("no such file on disk "+str(file))
            elif file_stat[0].has_key('headAction') and file_stat[0]['headAction'] == "delete":
               if os.access(file, os.F_OK):
                  self.LogLine("adding file to perforce "+str(file))
                  self.p4.run("add", "-f", file)
                  depotFiles.append(file_stat[0]['depotFile'])
            elif not self.IsCheckoutedByUser(file):
               if os.access(file, os.F_OK):
                  self.p4.run("revert", "-a", file)
                  if not self.IsCheckoutedByUser(file):
                     self.LogLine("Checkin "+str(file))
                     depotFiles.append(file_stat[0]['depotFile'])
                  else:
                     self.LogLine("Reverted, cause no change "+str(file))
               else:
                  self.p4.run("revert", file)
                  self.p4.run("delete", file)
                  self.LogLine("Delete from perforce "+str(file))
                  depotFiles.append(file_stat[0]['depotFile'])
            else:
               self.LogLine("No actions for file "+str(file))
     
      if self.p4:
         if len(depotFiles) > 0:
            spec = self.p4.fetch_change()
           
            spec[ "Description" ] = comment
            spec._files = depotFiles
            
            self.p4.save_submit(spec)     
            self.LogResult()
      
      return self.HasErrors()

   def Revert (self, files, bypassChange = False):
      files = files.replace('\\', '/')
      fileList = files.split(" ")
      for file in fileList:
         if not self.p4:
            #make readonly
            if not os.access(file, os.F_OK):
               self.LogLine("Revert file "+file+" doesn't exsist")
            else:
               stats = os.stat(file)
               if len(stats) > 0:
                  self.LogLine("Revert file "+file+" (R\O set)")
                  os.chmod(file, stat.S_IREAD)
         elif not self.IsCheckoutedByUser(file):
            self.LogLine("Revert "+ file)
            if bypassChange:
               self.p4.run("revert", "-k", file)
            else:
               self.p4.run("revert", file)
            self.LogResult()
         
      return self.HasErrors()

   def DeleteFromPerforce (self, files, comment = "default"):
      files = files.replace('\\', '/')
      fileList = files.split(" ")
      depotFiles = []
      for file in fileList:
         if self.p4:
            file_stat = self.p4.run("fstat", file)
            if (len(file_stat) <= 0):
               self.LogLine("no such file in depot "+file)
            else:
               if not self.IsCheckoutedByUser(file):
                  self.p4.run("revert", file)
               self.p4.run("delete", file)
               self.LogLine("Delete from perforce "+str(file))
               depotFiles.append(file_stat[0]['depotFile'])
     
      if self.p4:
         if len(depotFiles) > 0:
            spec = self.p4.fetch_change()
           
            spec[ "Description" ] = comment
            spec._files = depotFiles
            
            self.p4.save_submit(spec)     
            self.LogResult()
      
      return self.HasErrors()
   
   def RevertIfUnchanged (self, files):
      files = files.replace('\\', '/')
      fileList = files.split(" ")
      for file in fileList:
         if not self.p4:
            self.LogLine("RevertIfUnchanged unsupported offline! file "+file)
         elif not self.IsCheckoutedByUser(file):
            self.LogLine("RevertIfUnchanged "+ file)
            self.p4.run("revert", "-a", file)
            self.LogResult()
         
      return self.HasErrors()

   def MarkChangelist (self, changelist, label, description):
      if self.p4:
         jobDesc = self.p4.run("job", "-o", label)
         if jobDesc:
            jobDesc = jobDesc[0]
            jobDesc["Status"] = "closed"
            jobDesc["Description"] = description
            
            self.p4.input = jobDesc
            self.p4.run("job", "-i")
            self.LogResult()
            self.p4.run("fix", "-c" + changelist, label)
            self.LogResult()

   def GetLatestChangeList (self):
      if not self.p4: 
         return "0"
      
      changeList = self.p4.run("changes", "-m1 //%s/..." % self.p4.client)
      return changeList[0]["change"]

   def GetChangeListDescription (self, changeList):
      if not self.p4:
         return None
      
      description = self.p4.run("describe", "-s", "%s" % changeList)
      return description

   def IsFileChanged (self, file, rev1, rev2):
      if not self.p4:
         return False

      diff = self.p4.run("diff2", file+"#"+rev1, file+"#"+rev2)
      if self.HasErrors():
         self.LogResults()
         return False
      
      if diff[0]["status"] == "identical":
         return False
      elif diff[0]["status"] == "content":
         return True
      elif diff[0]["status"] == "types":
         return False
      else:
         self.LogLine(" s3p4 error: Unknown result of diff2")
         return False

   def GetDepotFileRevision(self, file, isForce = True, changeList = "#head"):
      if not self.p4: 
         return None
         
      fileParam = file + changeList
      
      if self.useLogs and file != "":
         self.LogLine("Getting: "+str(fileParam))
      
      if isForce:
         res = self.p4.run("sync", "-f", fileParam)
      else:
         res = self.p4.run("sync", fileParam)

      self.LogResult()

      return res

   def EditWorkspace(self, path, pathSpecArray, p4Client = ""):
      if self.p4:
         if p4Client == "":
            p4Client = self.p4.client
         clientDesc = self.p4.run("client", "-o", p4Client)
         if clientDesc:
            clientDesc = clientDesc[0]
            clientDesc["Owner"]  = self.p4.user
            clientDesc["Root"]   = path
            clientDesc["View"]   = pathSpecArray
            
            self.p4.input = clientDesc
            res = self.p4.run("client", "-i")
            self.LogLine(str(res))
            
            self.LogResult()
            return True
         
         self.LogResult()
      
      return False

   def GetWorkspaceRoot(self, p4Client = ""):
      if self.p4:
         if p4Client == "":
            p4Client = self.p4.client
         clientDesc = self.p4.run("client", "-o", p4Client)
         self.LogResult()
         if clientDesc:
            clientDesc = clientDesc[0]
            return clientDesc["Root"]
      return ""

   def CreateBranch(self, branchName, pathSrcArray, pathDstArray):
      if self.p4:
         
         spec = self.p4.fetch_change()
         spec[ "Description" ] = "+auto+gravitas Automatic Branch"
         spec._files = []
         self.p4.input = spec
         res_str = self.p4.run_change("-i")[0]
         clistNum = res_str[res_str.find(' ') + 1:res_str.find(' created')]
         
         branchView = []
         for index in range(0,len(pathSrcArray)):
            branchView.append(pathSrcArray[index]+" "+pathDstArray[index])
            self.LogLine("integrate")
            self.p4.run("integrate", "-c", clistNum, "-v", pathSrcArray[index], pathDstArray[index])
            self.LogResult()
            if self.HasErrors():
               return False
               
         self.LogLine("submit")
         self.p4.run("submit", "-c" + clistNum)
         self.LogResult()
         if self.HasErrors():
            return False
         
         #create branch view
         branchDesc = (self.p4.run("branch", "-o", branchName))[0]
         branchDesc["Branch"] = branchName
         branchDesc["View"] = branchView
         
         self.p4.input = branchDesc
         self.p4.run("branch", "-i")
         if self.HasErrors():
            self.LogResult()
            return False
         
         self.LogResult()
         
      return True
      
   def ForceRevert(self, filename):
      if self.p4:
         fstat_res = self.p4.run("fstat", filename)
         self.LogResult()
         if len(fstat_res)>0:
            fstat_res = fstat_res[0]
            if fstat_res.has_key('otherOpens') and fstat_res.has_key('otherOpen'):
               self.LogLine("File '" + filename + "' checkouted by " + str(fstat_res['otherOpens']) + " user(s)")
               self.LogLine(str(fstat_res['otherOpen']))
               for userspec in fstat_res['otherOpen']:
                  p4revClient = userspec[userspec.find("@")+1:]

                  clientDesc = self.p4.run("client", "-o", p4revClient)
                  if clientDesc:
                     clientDesc = clientDesc[0]
                     
                     #remember workspace
                     oldhost = ""
                     if clientDesc.has_key('Host'):
                        oldhost = clientDesc["Host"]
                     
                     #patch workspace
                     clientDesc["Host"] = ""
                     self.p4.input = clientDesc
                     res = self.p4.run("client", "-i")
                     self.LogLine(str(res))
                     
                     #revert file
                     p4rev = s3dP4(self.p4.port, userspec[:userspec.find("@")], p4revClient, False, True)
                     p4rev.Revert(fstat_res['depotFile'], bypassChange = True)
                     p4rev.Term()
                  
                     #revert workspace
                     clientDesc["Host"] = oldhost
                     self.p4.input = clientDesc
                     res = self.p4.run("client", "-i")
                     self.LogLine(str(res))
